/**
 * 
 */
package siena.dbslayer;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import siena.SienaException;
import siena.logging.SienaLogger;
import siena.logging.SienaLoggerFactory;

/**
 * @author jsanca
 * 
 */
public class HttpClientImpl implements HttpClient {

	private Connector connector = null;

	private boolean useMethodTunnelingByDefault = false;
	
	protected SienaLogger logger = SienaLoggerFactory
			.getLogger(HttpClientImpl.class);

	/**
	 * 
	 */
	public HttpClientImpl(Connector connector) {

		this.connector = connector;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see siena.dbslayer.HttpClient#sendDelete(java.lang.Object,
	 *      siena.dbslayer.Serializer, siena.dbslayer.Serializer)
	 */
	public Object sendDelete(Object request, Serializer serializerIN,
			Serializer serializerOUT) {

		return this.sendMessageWithoutBody(request, serializerIN, serializerOUT,
				RequestMethod.DELETE, this.isUseMethodTunnelingByDefault());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see siena.dbslayer.HttpClient#sendGet(java.lang.Object,
	 *      siena.dbslayer.Serializer, siena.dbslayer.Serializer)
	 */
	public Object sendGet(Object request, Serializer serializerIN,
			Serializer serializerOUT) {
	
		return this.sendMessageWithoutBody(request, serializerIN, serializerOUT,
				RequestMethod.GET, this.isUseMethodTunnelingByDefault());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see siena.dbslayer.HttpClient#sendPost(java.lang.Object,
	 *      siena.dbslayer.Serializer, siena.dbslayer.Serializer)
	 */
	public Object sendPost(Object request, Serializer serializerIN,
			Serializer serializerOUT) {
	
		return this.sendMessageWithBody(request, serializerIN, serializerOUT,
				RequestMethod.POST, this.isUseMethodTunnelingByDefault());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see siena.dbslayer.HttpClient#sendPut(java.lang.Object,
	 *      siena.dbslayer.Serializer, siena.dbslayer.Serializer)
	 */
	public Object sendPut(Object request, Serializer serializerIN,
			Serializer serializerOUT) {

		return this.sendMessageWithBody(request, serializerIN, serializerOUT,
				RequestMethod.PUT, this.isUseMethodTunnelingByDefault());
	}

	protected Object sendMessageWithoutBody(Object request,
			Serializer serializerIN, Serializer serializerOUT,
			RequestMethod method, boolean useMethodTunneling) {

		Object response = null;
		ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();

		try {

			serializerIN.serialize(request, arrayOutputStream);

			this.connector.connect(method, useMethodTunneling,
					arrayOutputStream.toString().trim());

			response = serializerOUT.deserialize(this.connector
					.getInputStream());
		} catch (IOException e) {

			logger.severe(e, e);
			throw new SienaException(e);
		} finally {

			try {

				this.connector.close();
			} catch (IOException e) {

				logger.severe(e, e);
				throw new SienaException(e);
			}
		}

		return response;
	} // sendMessageWithoutBody.

	/**
	 * This is the main method to send and receive streams to some network
	 * place.
	 * 
	 * @param request
	 *            Object object to send
	 * @param serializerIN
	 *            {@link Serializer} serializer to convert the request object in
	 *            the stream
	 * @param serializerOUT
	 *            {@link Serializer} serializer to convert the response stream
	 *            in object
	 * @param method
	 *            {@link RequestMethod} method used to send by default POST
	 * @param useMethodTunneling
	 *            boolean if it is true, send the message by POST overriding the
	 *            method.
	 * @return Object response.
	 */
	protected Object sendMessageWithBody(Object request,
			Serializer serializerIN, Serializer serializerOUT,
			RequestMethod method, boolean useMethodTunneling) {

		Object response = null;

		try {

			this.connector.connect(method, useMethodTunneling);
			serializerIN.serialize(request, this.connector.getOutputStream());
			response = serializerOUT.deserialize(this.connector
					.getInputStream());
		} catch (IOException e) {

			logger.severe(e, e);
			throw new SienaException(e);
		} finally {

			try {

				this.connector.close();
			} catch (IOException e) {

				logger.severe(e, e);
				throw new SienaException(e);
			}
		}

		return response;
	} // sendMessageWithBody.

	public boolean isUseMethodTunnelingByDefault() {
		return useMethodTunnelingByDefault;
	}

	public void setUseMethodTunnelingByDefault(
			boolean useMethodTunnelingByDefault) {
		this.useMethodTunnelingByDefault = useMethodTunnelingByDefault;
	}
} // E:O:F:HttpClientImpl.
